package com.ecmcloud.service.impl;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ecmcloud.api.ApiStrategyFactory;
import com.ecmcloud.common.constants.FormTypeEnum;
import com.ecmcloud.common.constants.NodeUserTypeEnum;
import com.ecmcloud.common.constants.ProcessInstanceConstant;
import com.ecmcloud.common.dto.PageDto;
import com.ecmcloud.common.dto.PageResultDto;
import com.ecmcloud.common.dto.ProcessInstanceParamDto;
import com.ecmcloud.common.dto.Result;
import com.ecmcloud.common.dto.TaskDto;
import com.ecmcloud.common.dto.TaskQueryParamDto;
import com.ecmcloud.common.dto.flow.Node;
import com.ecmcloud.common.dto.third.UserDto;
import com.ecmcloud.common.utils.JsonMapperUtils;
import com.ecmcloud.constants.NodeStatusEnum;
import com.ecmcloud.entity.Process;
import com.ecmcloud.entity.ProcessInstanceAssignUserRecord;
import com.ecmcloud.entity.ProcessInstanceCopy;
import com.ecmcloud.entity.ProcessInstanceNodeRecord;
import com.ecmcloud.entity.ProcessInstanceRecord;
import com.ecmcloud.service.IProcessInstanceAssignUserRecordService;
import com.ecmcloud.service.IProcessInstanceCopyService;
import com.ecmcloud.service.IProcessInstanceNodeRecordService;
import com.ecmcloud.service.IProcessInstanceRecordService;
import com.ecmcloud.service.IProcessInstanceService;
import com.ecmcloud.service.IProcessService;
import com.ecmcloud.utils.CoreHttpUtil;
import com.ecmcloud.utils.NodeFormatUtil;
import com.ecmcloud.vo.FormItemVO;
import com.ecmcloud.vo.NodeFormatParamVo;
import com.ecmcloud.vo.ProcessInstanceCopyVo;
import com.ecmcloud.vo.node.NodeVo;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;

/**
 * 实例进程服务
 */
@Service
public class ProcessInstanceServiceImpl implements IProcessInstanceService {
		
	@Autowired
    private IProcessInstanceRecordService processInstanceRecordService;
    
	@Autowired
    private IProcessInstanceCopyService processCopyService;

	@Autowired
    private IProcessService processService;
    
	@Autowired
    private IProcessInstanceNodeRecordService processNodeRecordService;
    
	@Autowired
    private IProcessInstanceAssignUserRecordService processNodeRecordAssignUserService;

    /**
     * 启动流程
     *
     * @param processInstanceParamDto
     * @return
     */
    @Override
    public Object startProcessInstance(ProcessInstanceParamDto processInstanceParamDto) {
        String userId = StpUtil.getLoginIdAsString();
        UserDto user = ApiStrategyFactory.getStrategy().getUser(userId);
        
        processInstanceParamDto.setStartUserId(String.valueOf(userId));
        Map<String, Object> paramMap = processInstanceParamDto.getParamMap();
        Dict rootUser = Dict.create().set("id", userId).set("name", user.getName()).set("type", NodeUserTypeEnum.USER.getKey());
        paramMap.put("root", CollUtil.newArrayList(rootUser));

        String post = CoreHttpUtil.startProcess(processInstanceParamDto);
        Result<String> r = JsonMapperUtils.getInstance().parseObject(post, new TypeReference<Result<String>>() {});
        if (!r.isOk()) {
            return Result.fail(r.getMsg());
        }
        String data = r.getData();
        
        return Result.success(data);
    }

    /**
     * 查询当前登录用户的待办任务
     *
     * @param pageVO
     * @return
     */
    @Override
    public Object queryMineTask(PageDto pageVO) {
        TaskQueryParamDto taskQueryParamDto = BeanUtil.copyProperties(pageVO, TaskQueryParamDto.class);
        taskQueryParamDto.setAssign(StpUtil.getLoginIdAsString());
        Result<PageResultDto<TaskDto>> r = CoreHttpUtil.queryAssignTask(taskQueryParamDto);
        PageResultDto<TaskDto> pageResultDto = r.getData();
        List<TaskDto> records = pageResultDto.getRecords();
        if (CollUtil.isEmpty(records)) {
            return Result.success(pageResultDto);
        }

        Set<String> processInstanceIdSet = records.stream().map(w -> w.getProcessInstanceId()).collect(Collectors.toSet());
        //流程实例记录
        List<ProcessInstanceRecord> processInstanceRecordList = processInstanceRecordService.lambdaQuery().in(ProcessInstanceRecord::getProcessInstanceId,processInstanceIdSet).list();
        //发起人
        Set<String> startUserIdSet = processInstanceRecordList.stream().map(w -> w.getUserId()).collect(Collectors.toSet());

        List<UserDto> startUserList = Lists.newArrayList();
        for (String userIds : startUserIdSet) {
            UserDto user = ApiStrategyFactory.getStrategy().getUser(userIds);
            startUserList.add(user);
        }
        for (TaskDto record : records) {
            ProcessInstanceRecord processInstanceRecord = processInstanceRecordList.stream().filter(w -> StrUtil.equals(w.getProcessInstanceId(), record.getProcessInstanceId())).findAny().orElse(null);
            if (processInstanceRecord != null) {
                record.setProcessName(processInstanceRecord.getName());
                UserDto startUser = startUserList.stream().filter(w -> w.getId().equals( processInstanceRecord.getUserId())).findAny().orElse(null);

                record.setRootUserId(processInstanceRecord.getUserId());
                record.setGroupName(processInstanceRecord.getGroupName());
                record.setRootUserName(startUser.getName());
                record.setRootUserAvatarUrl(startUser.getAvatarUrl());
                record.setStartTime(processInstanceRecord.getCreateTime());
            }
        }
        return Result.success(pageResultDto);
    }

    /**
     * 查询已办任务
     *
     * @param pageVO
     * @return
     */
    @Override
    public Object queryMineEndTask(PageDto pageVO) {
        TaskQueryParamDto taskQueryParamDto = BeanUtil.copyProperties(pageVO, TaskQueryParamDto.class);
        taskQueryParamDto.setAssign(StpUtil.getLoginIdAsString());
        Result<PageResultDto<TaskDto>> r= CoreHttpUtil.queryCompletedTask(taskQueryParamDto);
        PageResultDto<TaskDto> pageResultDto = r.getData();
        List<TaskDto> records = pageResultDto.getRecords();
        if (CollUtil.isEmpty(records)) {
            return Result.success(pageResultDto);
        }
        Set<String> processInstanceIdSet = records.stream().map(w -> w.getProcessInstanceId()).collect(Collectors.toSet());
        //流程实例记录
        List<ProcessInstanceRecord> processInstanceRecordList = processInstanceRecordService.lambdaQuery().in(ProcessInstanceRecord::getProcessInstanceId, processInstanceIdSet).list();

        //发起人
        Set<String> startUserIdSet = processInstanceRecordList.stream().map(w -> w.getUserId()).collect(Collectors.toSet());
        List<UserDto> startUserList = Lists.newArrayList();
        for (String userIds : startUserIdSet) {
            UserDto user = ApiStrategyFactory.getStrategy().getUser(userIds);
            startUserList.add(user);
        }

        for (TaskDto record : records) {
            ProcessInstanceRecord processInstanceRecord = processInstanceRecordList.stream().filter(w -> StrUtil.equals(w.getProcessInstanceId(),record.getProcessInstanceId())).findAny().orElse(null);
            if (processInstanceRecord != null) {
                record.setProcessName(processInstanceRecord.getName());
                UserDto startUser = startUserList.stream().filter(w -> w.getId().equals( processInstanceRecord.getUserId())).findAny().orElse(null);
                record.setRootUserId(processInstanceRecord.getUserId());
                record.setGroupName(processInstanceRecord.getGroupName());
                record.setRootUserName(startUser.getName());
                record.setRootUserAvatarUrl(startUser.getAvatarUrl());
                record.setStartTime(processInstanceRecord.getCreateTime());
            }
        }
        return Result.success(pageResultDto);
    }

    /**
     * 流程结束
     *
     * @param processInstanceParamDto
     * @return
     */
    @Override
    public Result<String> end(ProcessInstanceParamDto processInstanceParamDto) {
        processInstanceRecordService.lambdaUpdate()
                .set(processInstanceParamDto.getResult()!=null,ProcessInstanceRecord::getResult, processInstanceParamDto.getResult())
                .set(ProcessInstanceRecord::getEndTime, new Date())
                .set(ProcessInstanceRecord::getStatus, NodeStatusEnum.YJS.getCode())
                .eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceParamDto.getProcessInstanceId())
                .update(new ProcessInstanceRecord());
        return Result.success();
    }

    /**
     * 查询我发起的
     *
     * @param pageDto
     * @return
     */
    @Override
    public Object queryMineStarted(PageDto pageDto) {
        String userId = StpUtil.getLoginIdAsString();
        Page<ProcessInstanceRecord> instanceRecordPage = processInstanceRecordService.lambdaQuery()
                .eq(ProcessInstanceRecord::getUserId, userId)
                .orderByDesc(ProcessInstanceRecord::getCreateTime)
                .page(new Page<>(pageDto.getPageNum(), pageDto.getPageSize()));
        return Result.success(instanceRecordPage);
    }

    /**
     * 查询抄送给我的
     *
     * @param pageDto
     * @return
     */
    @SuppressWarnings("unchecked")
	@Override
    public Object queryMineCC(PageDto pageDto) {
        String userId = StpUtil.getLoginIdAsString();
        Page<ProcessInstanceCopy> page = processCopyService.lambdaQuery()
                .eq(ProcessInstanceCopy::getUserId, userId)
                .orderByDesc(ProcessInstanceCopy::getNodeTime)
                .page(new Page<>(pageDto.getPageNum(), pageDto.getPageSize()));

        List<ProcessInstanceCopy> records = page.getRecords();
        List<ProcessInstanceCopyVo> processCopyVoList = BeanUtil.copyToList(records, ProcessInstanceCopyVo.class);
        if (CollUtil.isNotEmpty(records)) {
            //发起人
            Set<String> startUserIdSet = records.stream().map(w -> w.getStartUserId()).collect(Collectors.toSet());
            List<UserDto> startUserList = Lists.newArrayList();
            for (String s : startUserIdSet) {
                UserDto user = ApiStrategyFactory.getStrategy().getUser(s);
                startUserList.add(user);
            }
            for (ProcessInstanceCopyVo record : processCopyVoList) {
                UserDto startUser = startUserList.stream().filter(w -> w.getId().equals(record.getStartUserId())).findAny().orElse(null);
                record.setStartUserName(startUser.getName());
            }
        }
        Page<ProcessInstanceCopyVo> p = BeanUtil.copyProperties(page, Page.class);
        p.setRecords(processCopyVoList);
        return Result.success(p);
    }

    /**
     * 显示流程实例图片
     *
     * @param procInsId
     * @return
     */
    @Override
    public Object showImg(String procInsId) {
        String s = CoreHttpUtil.showImg(procInsId);
        Result<String> stringR = JsonMapperUtils.getInstance().parseObject(s, new TypeReference<Result<String>>() {});
        String data = stringR.getData();
        return Result.success(data);
    }

    /**
     * 格式化流程显示
     *
     * @param nodeFormatParamVo
     * @return
     */
    @Override
    public Object formatStartNodeShow(NodeFormatParamVo nodeFormatParamVo) {
        String flowId = nodeFormatParamVo.getFlowId();
        String processInstanceId = nodeFormatParamVo.getProcessInstanceId();
        if (StrUtil.isAllBlank(flowId, processInstanceId)) {
            return Result.success(Lists.newArrayList());
        }
        if (StrUtil.isBlankIfStr(flowId) && StrUtil.isNotBlank(processInstanceId)) {
            ProcessInstanceRecord processInstanceRecord = processInstanceRecordService.lambdaQuery().eq(ProcessInstanceRecord::getProcessInstanceId,processInstanceId).one();
            flowId = processInstanceRecord.getFlowId();
        }
        Map<String, Object> paramMap = nodeFormatParamVo.getParamMap();
        if (StrUtil.isNotBlank(nodeFormatParamVo.getTaskId())) {
            String s = CoreHttpUtil.queryTaskVariables(nodeFormatParamVo.getTaskId(), null);
            Result<Map<String, Object>> r = JsonMapperUtils.getInstance().parseObject(s, new TypeReference<Result<Map<String, Object>>>() {});
            if (!r.isOk()) {
                List<ProcessInstanceAssignUserRecord> list = processNodeRecordAssignUserService.lambdaQuery()
                        .eq(ProcessInstanceAssignUserRecord::getTaskId, nodeFormatParamVo.getTaskId())
                        .eq(ProcessInstanceAssignUserRecord::getStatus, NodeStatusEnum.YJS.getCode())
                        .orderByDesc(ProcessInstanceAssignUserRecord::getEndTime)
                        .list();
                String data = list.get(0).getData();
                Map<String, Object> variableMap = JsonMapperUtils.getInstance().parseObject(data, new TypeReference<Map<String, Object>>() {});
                variableMap.putAll(paramMap);
                paramMap.putAll(variableMap);
            }else{
                Map<String, Object> variableMap = r.getData();
                variableMap.putAll(paramMap);
                paramMap.putAll(variableMap);
            }

        }

        Set<String> completeNodeSet = Sets.newConcurrentHashSet();
        if(StrUtil.isNotBlank(processInstanceId)){
            List<ProcessInstanceNodeRecord> processInstanceNodeRecordList = processNodeRecordService.lambdaQuery()
                    .eq(ProcessInstanceNodeRecord::getProcessInstanceId, processInstanceId)
                    .eq(ProcessInstanceNodeRecord::getStatus, NodeStatusEnum.YJS.getCode())
                    .list();
            Set<String> collect = processInstanceNodeRecordList.stream().map(w -> w.getNodeId()).collect(Collectors.toSet());
            completeNodeSet.addAll(collect);
        }


        Process oaForms = processService.getByFlowId(flowId);
        String process = oaForms.getProcess();
        Node nodeDto = JsonMapperUtils.getInstance().fromJson(process, Node.class);
        List<NodeVo> processNodeShowDtos = NodeFormatUtil.formatProcessNodeShow(nodeDto, completeNodeSet,Sets.newConcurrentHashSet(), processInstanceId, paramMap);
        return Result.success(processNodeShowDtos);
    }

    /**
     * 流程详情
     *
     * @param processInstanceId
     * @return
     */
    @Override
    public Object detail(String processInstanceId) {
        ProcessInstanceRecord processInstanceRecord = processInstanceRecordService.lambdaQuery().eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId).one();
        Process oaForms = processService.getByFlowId(processInstanceRecord.getFlowId());
        if (oaForms == null) {
            return Result.fail("流程不存在");
        }
        //发起人变量数据
        String formData = processInstanceRecord.getFormData();
        Map<String, Object> variableMap = JsonMapperUtils.getInstance().parseObject(formData, new TypeReference<Map<String, Object>>() {});
        //发起人表单权限
        String process = oaForms.getProcess();
        Node nodeDto = JsonMapperUtils.getInstance().fromJson(process, Node.class);
        Map<String, String> formPerms1 = nodeDto.getFormPerms();
        
        List<FormItemVO> jsonObjectList = JsonMapperUtils.getInstance().parseObject(oaForms.getFormItems(), new TypeReference<List<FormItemVO>>() {});
        for (FormItemVO formItemVO : jsonObjectList) {
            String id = formItemVO.getId();
            String perm = formPerms1.get(id);
            
            formItemVO.setPerm(StrUtil.isBlankIfStr(perm)? ProcessInstanceConstant.FormPermClass.READ:StrUtil.equals(perm,ProcessInstanceConstant.FormPermClass.HIDE)?perm:ProcessInstanceConstant.FormPermClass.READ);

            if(formItemVO.getType().equals(FormTypeEnum.LAYOUT.getType())){
                //明细
                List<Map<String, Object>> subParamList = MapUtil.get(variableMap, id, new cn.hutool.core.lang.TypeReference<List<Map<String, Object>>>() {});
                Object value = formItemVO.getProps().getValue();
                List<List<FormItemVO>> l= Lists.newArrayList();
                for (Map<String, Object> map : subParamList) {
                    List<FormItemVO> subItemList = Convert.toList(FormItemVO.class, value);
                    for (FormItemVO itemVO : subItemList) {
                        itemVO.getProps().setValue(map.get(itemVO.getId()));
                        String permSub = formPerms1.get(itemVO.getId());
                        itemVO.setPerm(StrUtil.isBlankIfStr(permSub)? ProcessInstanceConstant.FormPermClass.READ:StrUtil.equals(permSub,ProcessInstanceConstant.FormPermClass.HIDE)? permSub:ProcessInstanceConstant.FormPermClass.READ);
                    }
                    l.add(subItemList);
                }
                formItemVO.getProps().setValue(l);
            }else{
                formItemVO.getProps().setValue(variableMap.get(id));
            }
        }
        Dict set = Dict.create().set("processInstanceId", processInstanceId).set("process", oaForms.getProcess()).set("formItems", jsonObjectList);
        return Result.success(set);
    }
}
